package com.sun.filesigndemo.common.conf;


import com.sun.filesigndemo.common.security.Filter.FormAuthenticationFilter;
import com.sun.filesigndemo.common.security.Filter.UserAuthFilter;
import com.sun.filesigndemo.common.security.Realm.SystemAuthorizingRealm;
import com.sun.filesigndemo.common.security.Realm.UserTokenAuthorizingRealm;
import com.sun.filesigndemo.common.security.cache.JedisCacheManager;
import com.sun.filesigndemo.common.security.session.CacheSessionDAO;
import com.sun.filesigndemo.common.security.session.SessionDAO;
import com.sun.filesigndemo.common.security.session.SessionManager;
import com.sun.filesigndemo.common.utils.IdGen;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro 配置
 * @author Winkey
 * @version 2017-11-06
 * 
 */
@Configuration
public class ShiroConfig {

	

    public static final String ACTIVE_SESSION_CACHE_NAME = "activeSessionsCache";
    public static final String SESSION_NAME = "winkey.id";
    public static final String CACHE_KEY_PREFIX = "shiro_cache_";
	
	/**
	 * ShiroFilterFactoryBean 处理拦截资源文件问题。
	 * 注意：单独一个ShiroFilterFactoryBean配置是或报错的，以为在
	 * 初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager
	 *
	 * Filter Chain定义说明 1、一个URL可以配置多个Filter，使用逗号分隔 2、当设置多个过滤器时，全部验证通过，才视为通过
	 * 3、部分过滤器可指定参数，如perms，roles
	 *
	 */
	@Bean("shiroFilter")
	public FilterRegistrationBean shiroFilter(SecurityManager securityManager, FormAuthenticationFilter formAuthenticationFilter,
			UserAuthFilter userAuthFilter) throws Exception {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

		FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
		// 必须设置 SecurityManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);

		// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
		shiroFilterFactoryBean.setLoginUrl("/login");
		// 登录成功后要跳转的链接
		shiroFilterFactoryBean.setSuccessUrl("/success");
		// 未授权界面;
//		shiroFilterFactoryBean.setUnauthorizedUrl("/403");
		
		//自定义拦截器
		Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
		filtersMap.put("authc", formAuthenticationFilter);
		filtersMap.put("userinfo", userAuthFilter);

		shiroFilterFactoryBean.setFilters(filtersMap);
		
		// 权限控制map.
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

		filterChainDefinitionMap.put("/logout", "anon");
		filterChainDefinitionMap.put("/api/**", "userinfo");
		filterChainDefinitionMap.put("/test/**", "anon");
		//新增公共接口 任何人都可以访问
		filterChainDefinitionMap.put("/public/**", "anon");

		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

		filterRegistration.addInitParameter("targetFilterLifecycle", "true");
		filterRegistration.setFilter((Filter) shiroFilterFactoryBean.getObject());
		return filterRegistration;

	}

	@Bean
	public SecurityManager securityManager(SystemAuthorizingRealm systemAuthorizingRealm, UserTokenAuthorizingRealm userTokenAuthorizingRealm,
										   CacheManager jedisCacheManager, SessionManager sessionManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		Collection<Realm> realms = new ArrayList<>();
		realms.add(systemAuthorizingRealm);
		realms.add(userTokenAuthorizingRealm);
		// 设置realm.
		securityManager.setRealms(realms);
		// 自定义缓存实现 使用redis
		securityManager.setCacheManager(jedisCacheManager);
		// 自定义session管理 使用redis
		securityManager.setSessionManager(sessionManager);
		return securityManager;
	}

	/**
	 * cacheManager 缓存 redis实现
	 * @return
	 */
	@Bean("jedisCacheManager")
	public JedisCacheManager jedisCacheManager() {
		JedisCacheManager jedisCacheManager = new JedisCacheManager();
		jedisCacheManager.setCacheKeyPrefix(CACHE_KEY_PREFIX);
		return jedisCacheManager;
	}
//
//	@Bean
//	public FilterRegistrationBean delegatingFilterProxy(){
//	    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
//	    DelegatingFilterProxy proxy = new DelegatingFilterProxy();
//	    proxy.setTargetFilterLifecycle(true);
//	    proxy.setTargetBeanName("shiroFilter");
//	    filterRegistrationBean.setFilter(proxy);
//		filterRegistrationBean.setEnabled(false);
//	    return filterRegistrationBean;
//	}

	/**
	 * Session Manager
	 */
	@Bean
	public SessionManager sessionManager(SessionDAO sessionDAO, SimpleCookie sessionIdCookie) {
		SessionManager sessionManager = new SessionManager();
		sessionManager.setSessionDAO(sessionDAO);
		//会话超时时间，单位：毫秒
		sessionManager.setGlobalSessionTimeout(14400000);
		//定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话
		sessionManager.setSessionValidationInterval(3600000);
		sessionManager.setSessionValidationSchedulerEnabled(true);
		sessionManager.setSessionIdCookie(sessionIdCookie);
		sessionManager.setSessionIdCookieEnabled(true);
		return sessionManager;
	}
	
	@Bean
	public SessionDAO sessionDAO(IdGen idGen, CacheManager jedisCacheManager) {
		CacheSessionDAO sessionDAO= new CacheSessionDAO();
		sessionDAO.setSessionIdGenerator(idGen);
		sessionDAO.setActiveSessionsCacheName(ACTIVE_SESSION_CACHE_NAME);
		sessionDAO.setCacheManager(jedisCacheManager);
		return sessionDAO;
	}
	
	/**
	 * 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,
	 * 当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失!
	 * **/
	@Bean
	public SimpleCookie sessionIdCookie() {
		SimpleCookie simpleCookie= new SimpleCookie();
		simpleCookie.setName(SESSION_NAME);
		return simpleCookie;
	}
    
    /**
     * 保证实现了Shiro内部lifecycle函数的bean执行
     * 
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    		LifecycleBeanPostProcessor lifecycleBeanPostProcessor= new LifecycleBeanPostProcessor();
    		return lifecycleBeanPostProcessor;
    }
    
    /**
     * AOP式方法级权限检查
     * @return
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    		DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator= new DefaultAdvisorAutoProxyCreator();
    		defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
    		return defaultAdvisorAutoProxyCreator;
    }
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor= new AuthorizationAttributeSourceAdvisor();
    		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    		return authorizationAttributeSourceAdvisor;
    	
    }
    
}
